home *** CD-ROM | disk | FTP | other *** search
/ Amiga Game-Power / Amiga Game-Power.iso / pd mix ii / access / hddriver / driver / wd.c < prev   
C/C++ Source or Header  |  1994-05-20  |  12KB  |  635 lines

  1.  
  2. /*
  3.  * Copyright 1987 Alan Kent
  4.  *
  5.  * Permission is granted to redistribute this code as long
  6.  * as this message is retained in the code and the code is
  7.  * not sold without written permission from the author.
  8.  *
  9.  * UUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk
  10.  * ACSnet: ajk@goanna.oz
  11.  * ARPA: munnari!goanna.oz!ajk@SEISMO.ARPA
  12.  */
  13.  
  14. #include "hd.h"
  15. #include <hardware/custom.h>
  16. #include <hardware/intbits.h>
  17.  
  18.  
  19.  
  20. extern void rdsec ();
  21. extern void wrsec ();
  22.  
  23. extern int        init_irq ();
  24. extern void        free_irq ();
  25. extern void        wait_for_irq ();
  26. extern void        set_cmd_issued ();
  27.  
  28. extern ULONG *    NewHashTable ();
  29. extern void        FreeHashTable ();
  30. extern void        AddHashTable ();
  31. extern LONG        TestHashTable ();
  32.  
  33.  
  34. ULONG *            bad_hash_table;
  35.  
  36.  
  37. struct {
  38.     LONG            cmd_issued;
  39.     struct Task *    task;
  40.     LONG            sig_bits;
  41. } data;
  42.  
  43.  
  44.  
  45. struct wd1002 {
  46.     UBYTE    pad0;
  47.     UBYTE    data;
  48.     UBYTE    pad1;
  49.     UBYTE    error;
  50. #define write_precomp    error
  51.     UBYTE    pad2;
  52.     UBYTE    sec_count;
  53.     UBYTE    pad3;
  54.     UBYTE    sec_num;
  55.     UBYTE    pad4;
  56.     UBYTE    cyl_low;
  57.     UBYTE    pad5;
  58.     UBYTE    cyl_high;
  59.     UBYTE    pad6;
  60.     UBYTE    select;
  61.     UBYTE    pad7;
  62.     UBYTE    status;
  63. #define cmd_reg        status
  64. };
  65.  
  66.  
  67. #define WD        ((struct wd1002 *)0x9ffff0)
  68.  
  69.  
  70. #define WDS_BUSY        0x80
  71. #define WDS_READY        0x40
  72. #define WDS_WRFAULT        0x20
  73. #define WDS_SEEKDONE    0x10
  74. #define WDS_DRQ            0x08
  75. #define WDS_ERRCORRECTED 0x04
  76. #define WDS_ERROR        0x01
  77.  
  78. #define WDE_BADBLOCK    0x80
  79. #define WDE_CRCERROR    0x40
  80. #define WDE_IDNOTFOUND    0x10
  81. #define WDE_ABORT        0x04
  82. #define WDE_TR0ERROR    0x02
  83. #define WDE_DAMERROR    0x01
  84.  
  85. #define WDC_RESTORE        0x10
  86. #define WDC_STEPSPEED    0x00
  87. #define WDC_SEEK        0x70
  88. #define WDC_READ        0x20
  89. #define WDC_WRITE        0x30
  90. #define WDC_FORMAT        0x50
  91.  
  92. #define WDC_MULTIPLE    0x04
  93.  
  94. /* drive 1, ECC, 512 byte sectors */
  95. #define WD_SELECT        0xa0
  96. /* deselct by selecting drive 2 */
  97. #define WD_DESELECT        0xa8
  98.  
  99. #define WD_HEAD            0x03
  100.  
  101.  
  102. static int wd_head;
  103.  
  104.  
  105. int
  106. wd_open ()
  107. {
  108.     int status;
  109.     char dummy;
  110.     struct posn posn;
  111.     UBYTE *p;
  112.     int i;
  113.  
  114.     /* first make sure its plugged in!!! */
  115.  
  116.     WD->sec_count = 0;
  117.     if ( WD->sec_count != 0 )
  118.         return ( -1 );
  119.     WD->sec_count = 56;
  120.     if ( WD->sec_count != 56 )
  121.         return ( -1 );
  122.  
  123.     /* initialize hash table for quick bad sector map checking */
  124.  
  125.     bad_hash_table = NewHashTable ( 4000L );
  126.     if ( bad_hash_table == NULL )
  127.         return ( -1 );
  128.  
  129.     /* initialize interrupts */
  130.  
  131.     if ( init_irq () < 0 ) {
  132.         FreeHashTable ( bad_hash_table );
  133.         return ( -1 );
  134.     }
  135.  
  136.     /* select the drive */
  137.  
  138.     WD->select = WD_SELECT;    
  139.  
  140.     busy_wait ();
  141.     while ( WD->status & WDS_DRQ ) {
  142.         dummy = WD->data;
  143.         WD->data = 0;
  144.     }
  145.     WD->write_precomp = 32;
  146.     WD->sec_count = 1;
  147.     WD->cyl_low = 0;
  148.     WD->cyl_high = 0;
  149.     WD->select = WD_SELECT;
  150.     wd_head = 0;
  151.     status = wd_cmd ( WDC_RESTORE | WDC_STEPSPEED , TRUE );
  152.     if ( status == 0 ) {
  153.         deselect ();
  154.  
  155.         /* ok, now read in the header sectors */
  156.  
  157.         posn.sector = 0;
  158.         posn.block = 0;
  159.         posn.surface = 0;
  160.         posn.cylinder = 0;
  161.         p = (UBYTE*) &first;
  162.         status = read_sector ( &posn , p );
  163.         if ( status == 0 ) {
  164.             if ( first.magic != HD_MAGIC ) {
  165.  
  166.                 /* no magic sectors at beginning. default something */
  167.  
  168.                 first.sectors = 16;
  169.                 first.heads = 4;
  170.                 first.cylinders = 480;
  171.                 first.bad_sectors = 0;
  172.                 first.park_cylinder = 512;
  173.                 first.map_sectors = 0;
  174.             }
  175.             else {
  176.  
  177.                 /* read in rest of first structure */
  178.  
  179.                 for ( posn.sector = 1;
  180.                 posn.sector < first.map_sectors;
  181.                 posn.sector++ ) {
  182.                     p += HD_SECTOR;
  183.                     posn.block = posn.sector;
  184.                     status = read_sector ( &posn , p );
  185.                     if ( status != 0 )
  186.                         break;
  187.                 }
  188.  
  189.                 /* if all ok, set up hash table for bad sectors */
  190.  
  191.                 if ( status == 0 ) {
  192.                     for ( i = 0; i < first.bad_sectors; i++ )
  193.                         AddHashTable ( bad_hash_table , first.map[i] );
  194.                 }
  195.             }
  196.         }
  197.     }
  198.     if ( status != 0 ) {
  199.         free_irq ();
  200.         FreeHashTable ( bad_hash_table );
  201.         return ( -1 );
  202.     }
  203.     return ( 0 );
  204. }
  205.  
  206.  
  207. void
  208. wd_close ()
  209. {
  210.     free_irq ();
  211.     FreeHashTable ( bad_hash_table );
  212. }
  213.  
  214.  
  215. int
  216. wd_cmd ( cmd , wait )
  217. int cmd;
  218. int wait;
  219. {
  220.     register int error;
  221.     register int status;
  222.     char dummy;
  223.  
  224.     WD->select = WD_SELECT | wd_head;
  225.     busy_wait ();
  226.     while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
  227.  
  228.     set_cmd_issued ();
  229.  
  230.     WD->cmd_reg = cmd;
  231.  
  232.     if ( wait ) {
  233.         wait_for_irq ();
  234.     }
  235.  
  236.     busy_wait ();
  237.     while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
  238.     error = WD->status;
  239.     status = 0;
  240.     if ( ( error & WDS_READY ) == 0 )
  241.         status = TDERR_NotSpecified;
  242.     else if ( error & WDS_WRFAULT )
  243.         status = TDERR_WriteProt;
  244.     else if ( error & WDS_ERROR ) {
  245.         error = WD->error;
  246.         if ( error & WDE_BADBLOCK )
  247.             status = TDERR_BadSecSum;
  248.         else if ( error & WDE_ABORT )
  249.             status = TDERR_NotSpecified;
  250.         else if ( error & WDE_TR0ERROR )
  251.             status = TDERR_SeekError;
  252.         else if ( error & WDE_DAMERROR )
  253.             status = TDERR_BadSecPreamble;
  254.         else if ( error & WDE_IDNOTFOUND )
  255.             status = TDERR_NoSecHdr;
  256.         else if ( error & WDE_CRCERROR )
  257.             status = TDERR_BadSecSum;
  258.         else
  259.             status = TDERR_NotSpecified;
  260.     }
  261.     if ( status != 0 ) {
  262.  
  263.         /* try and recover from error - dont leave registers in bad way */
  264.  
  265.         busy_wait ();
  266.         while ( WD->status & WDS_DRQ )
  267.             dummy = WD->data;
  268.  
  269.         /* force a read of the error register */
  270.         error = WD->error;
  271.  
  272.         while ( WD->status & WDS_DRQ )
  273.             dummy = WD->data;
  274.  
  275.         /* do a restore too */
  276.  
  277.         WD->select = WD_SELECT;
  278.         busy_wait ();
  279.         while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
  280.         set_cmd_issued ();
  281.         WD->cmd_reg = WDC_RESTORE;
  282.         wait_for_irq ();
  283.         busy_wait ();
  284.         while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
  285.         error = WD->status;
  286.  
  287.     }
  288.     return ( status );
  289. }
  290.  
  291.  
  292. int
  293. wd_seek ( posn )
  294. register struct posn *posn;
  295. {
  296.     int status;
  297.  
  298.     WD->select = WD_SELECT;
  299.     busy_wait ();
  300.     wd_head = posn->surface;
  301.     WD->cyl_low = posn->cylinder;
  302.     WD->cyl_high = posn->cylinder >> 8;
  303.     WD->sec_num = posn->sector;
  304.     status = wd_cmd ( WDC_SEEK , TRUE );
  305.     if ( status != 0 ) {
  306.         bad_error = status;
  307.         bad_posn = *posn;
  308.     }
  309.     else
  310.         deselect ();
  311.     return ( status );
  312. }
  313.  
  314.  
  315. wd_park ()
  316. {
  317.     int status;
  318.     int dummy;
  319.  
  320.     WD->select = WD_SELECT;
  321.     wd_head = 0;
  322.     busy_wait ();
  323.     WD->cyl_low = first.park_cylinder;
  324.     WD->cyl_high = first.park_cylinder >> 8;
  325.     WD->sec_num = 0;
  326.     status = wd_cmd ( WDC_SEEK , TRUE );
  327.     if ( status != 0  &&  bad_error == 0 ) {
  328.         bad_error = status;
  329.         bad_posn = cur_posn;
  330.     }
  331.  
  332.     /* flush anything else (or if error) */
  333.  
  334.     while ( WD->status & WDS_DRQ )
  335.         dummy = WD->data;
  336.  
  337.     /* if parked, dont deselect unless error */
  338.     /* (my drive's light turns yellow when parked) */
  339.  
  340.     if ( status != 0 )
  341.         deselect ();
  342.  
  343.     return ( status );
  344. }
  345.  
  346.  
  347. int
  348. read_sector ( posn , buf )
  349. struct posn *posn;
  350. UBYTE *buf;
  351. {
  352.     int status;
  353.     int dummy;
  354.  
  355.     cur_posn = *posn;
  356.     bad_remap ( &cur_posn );
  357.     WD->select = WD_SELECT;
  358.     wd_head = cur_posn.surface;
  359.     busy_wait ();
  360.     WD->cyl_low = cur_posn.cylinder;
  361.     WD->cyl_high = cur_posn.cylinder >> 8;
  362.     WD->sec_num = cur_posn.sector;
  363.     status = wd_cmd ( WDC_READ , TRUE );
  364.     if ( status != 0  &&  bad_error == 0 ) {
  365.         bad_error = status;
  366.         bad_posn = cur_posn;
  367.     }
  368.     if ( status == 0 )
  369.         rdsec ( buf , &WD->data , (LONG)HD_SECTOR );
  370.  
  371.     /* flush anything else (or if error) */
  372.  
  373.     while ( WD->status & WDS_DRQ )
  374.         dummy = WD->data;
  375.     deselect ();
  376.     return ( status );
  377. }
  378.  
  379.  
  380. #ifdef DAM_SECTOR_MAPPING
  381. /* This wont really work because of bad sector remapping */
  382. int
  383. read_track ( posn , buf )
  384. register struct posn *posn;
  385. UBYTE *buf;
  386. {
  387.     int status;
  388.     int dummy;
  389.     int sector;
  390.  
  391.     cur_posn = *posn;
  392.     bad_remap ( &cur_posn );
  393.     WD->select = WD_SELECT;
  394.     wd_head = cur_posn.surface;
  395.     busy_wait ();
  396.     WD->cyl_low = cur_posn.cylinder;
  397.     WD->cyl_high = cur_posn.cylinder >> 8;
  398.     for ( sector = 0; sector < first.sectors; sector++ ) {
  399.         WD->sec_num = sector;
  400.         status = wd_cmd ( WDC_READ , TRUE );
  401.         if ( status != 0  &&  bad_error == 0 ) {
  402.             bad_error = status;
  403.             bad_posn = cur_posn;
  404.         }
  405.         if ( status != 0 )
  406.             break;
  407.         rdsec ( buf , &WD->data , (LONG)HD_SECTOR );
  408.         buf += HD_SECTOR;
  409.     }
  410.  
  411.     /* flush anything else (or if error) */
  412.  
  413.     while ( WD->status & WDS_DRQ )
  414.         dummy = WD->data;
  415.     deselect ();
  416.     return ( status );
  417. }
  418. #endif
  419.  
  420.  
  421. /* this version simply does multiple calls to read_sector() */
  422. /* so the bad sector mapping should be ok */
  423. int
  424. read_track ( posn , buf )
  425. struct posn *posn;
  426. UBYTE *buf;
  427. {
  428.     int status;
  429.     struct posn save;
  430.     int sector;
  431.  
  432.     save = *posn;
  433.     for ( save.sector = 0; save.sector < first.sectors; save.sector++ ) {
  434.         save.block = save.sector + save.surface * first.sectors
  435.             + save.cylinder * first.heads * first.sectors;
  436.         status = read_sector ( &save , buf );
  437.         if ( status != 0 )
  438.             break;
  439.         buf += HD_SECTOR;
  440.     }
  441.     return ( status );
  442. }
  443.  
  444.  
  445. int
  446. write_sector ( posn , buf )
  447. register struct posn *posn;
  448. char *buf;
  449. {
  450.     int status;
  451.     int dummy;
  452.  
  453.     if ( posn->cylinder < 0 ) {
  454.         return;
  455.     }
  456.     cur_posn = *posn;
  457.     bad_remap ( &cur_posn );
  458.     WD->select = WD_SELECT;
  459.     wd_head = cur_posn.surface;
  460.     busy_wait ();
  461.     WD->cyl_low = cur_posn.cylinder;
  462.     WD->cyl_high = cur_posn.cylinder >> 8;
  463.     WD->sec_num = cur_posn.sector;
  464.     status = wd_cmd ( WDC_WRITE , FALSE );
  465.     if ( status != 0  &&  bad_error == 0 ) {
  466.         bad_error = status;
  467.         bad_posn = cur_posn;
  468.     }
  469.     if ( status == 0 )
  470.         wrsec ( buf , &WD->data , (LONG)HD_SECTOR );
  471.  
  472.     /* Flush buffer in case of error */
  473.  
  474.     while ( WD->status & WDS_DRQ )
  475.         dummy = WD->data;
  476.  
  477.     wait_for_irq ();
  478.  
  479.     deselect ();
  480.     return ( status );
  481. }
  482.  
  483.  
  484. int
  485. wd_format_track ( posn )
  486. register struct posn *posn;
  487. {
  488.     register int i;
  489.     int dummy;
  490.  
  491.     WD->sec_num = 0;
  492.     WD->cyl_low = posn->cylinder;
  493.     WD->cyl_high = posn->cylinder >> 8;
  494.     WD->select = WD_SELECT | posn->surface;
  495.     WD->sec_count = first.sectors;
  496.  
  497.     set_cmd_issued ();
  498.     WD->cmd_reg = WDC_FORMAT;   /* format track */
  499.  
  500.     busy_wait ();
  501.     while ( ( WD->status & WDS_SEEKDONE ) == 0 );
  502.     while ( ( WD->status & WDS_DRQ ) == 0 );
  503.     wrsec ( first.interleave , &WD->data , (LONG)HD_SECTOR );
  504.     wait_for_irq ();
  505.     deselect ();
  506.     return ( 0 );
  507. }
  508.  
  509.  
  510. deselect ()
  511. {
  512.     busy_wait ();
  513.     WD->select = WD_DESELECT;
  514. }
  515.  
  516.  
  517. busy_wait ()
  518. {
  519.     while ( WD->status & WDS_BUSY )
  520.         /*Delay ( 1L )*/;
  521. }
  522.  
  523.  
  524. bad_remap ( posn )
  525. register struct posn *posn;
  526. {
  527.     register int i;
  528.  
  529.  
  530.     if ( ! TestHashTable ( bad_hash_table , posn->block ) )
  531.         return;
  532.  
  533.     for ( i = 0; i < first.bad_sectors; i++ ) {
  534.         if ( first.map[i] == posn->block ) {
  535.             posn->block = i + HD_MAP_SECTORS;
  536.             posn->sector = posn->block % first.sectors;
  537.             posn->surface = ( posn->block / first.sectors ) % first.heads;
  538.             posn->cylinder = posn->block / ( first.sectors * first.heads );
  539.             break;
  540.         }
  541.     }
  542. }
  543.  
  544.  
  545.  
  546.  
  547. /************************ Interrupt handling code ***********************/
  548.  
  549. /* change task that should get sent interrupt signals */
  550.  
  551. wd_subtask ( task )
  552. struct Task *task;
  553. {
  554.     data.task = task;
  555. }
  556.  
  557.  
  558. int
  559. init_irq ()
  560. {
  561.  
  562.     /* set up data structure */
  563.  
  564.     data.cmd_issued = 0;
  565.     data.task = FindTask ( 0L );
  566.     data.sig_bits = AllocSignal ( -1L );
  567.     if ( data.sig_bits < 0 ) {
  568.         return ( -1 );
  569.     }
  570.     data.sig_bits = 1L << data.sig_bits;
  571.  
  572.     /* allocate interrupt structure node */
  573.  
  574.     HDInterrupt = (struct Interrupt *)
  575.         AllocMem ( (LONG)sizeof ( struct Interrupt ) , (LONG)MEMF_PUBLIC );
  576.     if ( HDInterrupt == NULL ) {
  577.         return ( -1 );
  578.     }
  579.  
  580.     /* set up interrupt structure flags */
  581.  
  582.     HDInterrupt->is_Node.ln_Type = NT_INTERRUPT;
  583.     HDInterrupt->is_Node.ln_Pri = 0;
  584.     HDInterrupt->is_Node.ln_Name = "Hard Disk IRQ";
  585.     HDInterrupt->is_Data = (APTR) &data;
  586.     HDInterrupt->is_Code = (VOID(*)()) HDHandler;
  587.  
  588.     AddIntServer ( (LONG)INTB_PORTS , HDInterrupt );
  589.  
  590.     return ( 0 );
  591. }
  592.  
  593.  
  594. void
  595. free_irq ()
  596. {
  597.     if ( HDInterrupt != NULL ) {
  598.         RemIntServer ( (LONG)INTB_PORTS , HDInterrupt );
  599.         FreeMem ( HDInterrupt , (LONG)sizeof ( struct Interrupt ) );
  600.         HDInterrupt = NULL;
  601.     }
  602. }
  603.  
  604.  
  605. void
  606. set_cmd_issued ()
  607. {
  608.     /* clear spurious signals */
  609.  
  610.     SetSignal ( (LONG)0 , data.sig_bits );
  611.  
  612.     data.cmd_issued = 1;
  613. }
  614.  
  615.  
  616. void
  617. wait_for_irq ()
  618. {
  619.     Wait ( data.sig_bits );
  620. }
  621.  
  622.  
  623. LONG
  624. HDHandler ()
  625. {
  626.     if ( ! ( WD->status & WDS_BUSY ) ) {
  627.         if ( data.cmd_issued ) {
  628.             data.cmd_issued = 0;
  629.             Signal ( data.task , data.sig_bits );
  630.             return ( 1 );
  631.         }
  632.     }
  633.     return ( 0 );
  634. }
  635.